import { Injectable } from '@angular/core';
import { Action, State, StateContext, Store } from '@ngxs/store';
import { DemoEditComponent } from '../../pages/demo-edit/demo-edit.component';
import { ResetForm } from '@ngxs/form-plugin';
import { Clipboard } from '@angular/cdk/clipboard';
import { NzMessageService } from 'ng-zorro-antd/message';
import { OverlayService } from '../../shared/overlay/overlay.service';
import { HttpClient } from '@angular/common/http';
import { DemoAction } from './demo.action';
import { mergeMap, of, tap } from 'rxjs';
import { decompressFromBase64 } from 'lz-string';
import { MenuEditComponent } from '../../pages/menu-edit/menu-edit.component';
import { uuid } from '../../shared/utils';
import * as immutable from 'object-path-immutable';
import * as _ from 'lodash';
import { RouterSelector } from '../router/router.selector';
import { RouterService } from '../router/router.service';
import { SystemSelector } from '../system';

export interface DemoStateModel {
  id: number;
  editFlag: boolean;
  editType: string;
  editForm: any;
  menuEditFlag: boolean;
  menuEditType: string;
  menuEditForm: any;
  list: any[];
}

@State<DemoStateModel>({
  name: 'demo',
  defaults: {
    id: 1,
    editFlag: false,
    editType: '',
    editForm: {},
    menuEditFlag: false,
    menuEditType: '',
    menuEditForm: {},
    list: [],
  },
})
@Injectable({
  providedIn: 'root',
})
export class DemoState {
  constructor(
      private clipboard: Clipboard,
      private message: NzMessageService,
      private overlay: OverlayService,
      private http: HttpClient,
      private store: Store,
      private routerService: RouterService,
  ) {
  }

  @Action(DemoAction.EditDemo)
  EditDemo(ctx: StateContext<DemoStateModel>, data: DemoAction.EditDemo) {
    ctx.patchState({
      editFlag: true,
      editType: 'edit',
    });
    this.overlay.createMask(DemoEditComponent);
    return ctx.dispatch(new ResetForm({path: 'demo.editForm', value: data.data}));
  }

  @Action(DemoAction.EditDemoNew)
  EditDemoNew(ctx: StateContext<DemoStateModel>) {
    let state = ctx.getState();
    if (state.editFlag) return;
    ctx.patchState({
      editFlag: true,
      editType: 'new',
    });
    this.overlay.createMask(DemoEditComponent);
    return ctx.dispatch(new ResetForm({path: 'demo.editForm'}));
  }

  @Action(DemoAction.CloseDemo)
  closeDemo(ctx: StateContext<DemoStateModel>) {
    ctx.patchState({editFlag: false});
    return ctx.dispatch(new ResetForm({path: 'demo.editForm'}));
  }

  @Action(DemoAction.SaveDemo)
  SaveDemo(ctx: StateContext<DemoStateModel>) {
    let state = ctx.getState();
    if (state.editForm.status === 'INVALID') {
      return;
    }
    let url = this.store.selectSnapshot(RouterSelector.url);
    let result = url?.replace(/\/demo\/(.*)/, '$1');
    let index1 = _.findIndex(state.list, s => s.code === result);
    let model = state.editForm.model;
    if (!model.id) {
      model.id = uuid();
      state = immutable.push(state, ['list', index1, 'list'], model);
    } else {
      let index2 = _.findIndex(state.list[index1].list, (s: any) => s.id === model.id);
      state = immutable.set(state, ['list', index1, 'list', index2], model);
    }
    let isLogin = this.store.selectSnapshot(SystemSelector.isLogin);
    let request;
    if (isLogin) {
      let token = this.store.selectSnapshot(SystemSelector.token) || '';
      const headers = {
        'Authorization': token,
      };
      request = this.http.post(`/api/codeforfun-statistics-backend/data/saveDemo?name=${result}`, model, {headers});
    } else {
      request = of({});
    }
    return request.pipe(tap(() => {
      ctx.patchState(state);
      this.message.create('success', `保存成功`);
    }), mergeMap(() => ctx.dispatch(new DemoAction.CloseDemo())));
  }

  @Action(DemoAction.Delete)
  Delete(ctx: StateContext<DemoStateModel>, {data}: DemoAction.Delete) {
    let url = this.store.selectSnapshot(RouterSelector.url);
    let result = url?.replace(/\/demo\/(.*)/, '$1');
    let state = ctx.getState();
    let index1 = _.findIndex(state.list, s => s.code === result);
    let index2 = _.findIndex(state.list[index1].list, (s: any) => s.id === data.id);
    state = immutable.del(state, ['list', index1, 'list', index2]);
    let isLogin = this.store.selectSnapshot(SystemSelector.isLogin);
    let request;
    if (isLogin) {
      let token = this.store.selectSnapshot(SystemSelector.token) || '';
      const headers = {
        'Authorization': token,
      };
      request = this.http.delete(`/api/codeforfun-statistics-backend/data/deleteDemo?name=${state.list[index1].code}&id=${data.id}`, {headers});
    } else {
      request = of({});
    }
    return request.pipe(tap(() => {
      ctx.patchState(state);
    }));
  }

  @Action(DemoAction.ImportSetting)
  ImportSetting(ctx: StateContext<DemoStateModel>, {settingString}: DemoAction.ImportSetting) {
    let decompressFromBase;
    try {
      decompressFromBase = decompressFromBase64(settingString);
    } catch (e) {
      this.message.error(`解析失败，请尝试删除几个结尾的等号`);
      throw e;
    }
    let r = JSON.parse(decompressFromBase);
    ctx.patchState({list: r});
    this.message.success(`导入成功`);
    this.routerService.init();
  }

  @Action(DemoAction.ResetData)
  ResetData(ctx: StateContext<DemoStateModel>) {
    let isLogin = this.store.selectSnapshot(SystemSelector.isLogin);
    let request;
    if (isLogin) {
      let token = this.store.selectSnapshot(SystemSelector.token) || '';
      const headers = {
        'Authorization': token,
      };
      request = this.http.get(`/api/codeforfun-statistics-backend/data/list`, {headers});
    } else {
      request = of([]);
    }
    return request.pipe(tap((r: any) => {
      r.forEach((s: any) => {
        if (!s.id) s.id = uuid();
        if (!s.code) s.code = s.name;
      });
      ctx.patchState({list: r});
      this.routerService.init();
      this.message.success(`初始化成功`);
    }));
  }

  @Action(DemoAction.LoadMenu)
  LoadMenu(ctx: StateContext<DemoStateModel>) {
    let state = ctx.getState();
    let isLogin = this.store.selectSnapshot(SystemSelector.isLogin);
    if (!!state.list && state.list.length > 0 && !isLogin) {
      return;
    }
    return ctx.dispatch(new DemoAction.ResetData());
  }

  @Action(DemoAction.CreateMenu)
  CreateMenu(ctx: StateContext<DemoStateModel>) {
    ctx.patchState({
      menuEditType: 'new',
      menuEditFlag: true,
    });
    this.overlay.createMask(MenuEditComponent);
    return ctx.dispatch(new ResetForm({path: 'demo.menuEditForm'}));
  }

  @Action(DemoAction.EditMenu)
  EditMenu(ctx: StateContext<DemoStateModel>, {data}: DemoAction.EditMenu) {
    ctx.patchState({
      menuEditType: 'edit',
      menuEditFlag: true,
    });
    this.overlay.createMask(MenuEditComponent);
    return ctx.dispatch(new ResetForm({path: 'demo.menuEditForm', value: data}));
  }

  @Action(DemoAction.CloseMenu)
  CloseMenu(ctx: StateContext<DemoStateModel>) {
    ctx.patchState({
      menuEditType: '',
      menuEditFlag: false,
    });
  }

  @Action(DemoAction.SaveMenu)
  SaveMenu(ctx: StateContext<DemoStateModel>) {
    let state = ctx.getState();
    if (state.menuEditForm.status === 'INVALID') {
      return;
    }
    let model = state.menuEditForm.model;
    if (!model.id) {
      model.id = uuid();
      model.list = [];
      state = immutable.push(state, ['list'], model);
    } else {
      let idx = _.findIndex(state.list, s => s.id === model.id);
      model.list = state.list[idx].list;
      state = immutable.set(state, ['list', idx], model);
    }
    let isLogin = this.store.selectSnapshot(SystemSelector.isLogin);
    let request;
    if (isLogin) {
      let token = this.store.selectSnapshot(SystemSelector.token) || '';
      const headers = {
        'Authorization': token,
      };
      request = this.http.post(`/api/codeforfun-statistics-backend/data/saveMenu`, model, {headers});
    } else {
      request = of({});
    }
    return request.pipe(tap(() => {
      ctx.patchState(state);
      this.message.create('success', `保存成功`);
      this.routerService.init();
    }), mergeMap(() => ctx.dispatch(new DemoAction.CloseMenu())));
  }

  @Action(DemoAction.DeleteMenu)
  DeleteMenu(ctx: StateContext<DemoStateModel>, {data}: DemoAction.DeleteMenu) {
    let state = ctx.getState();
    let idx = _.findIndex(state.list, s => s.id === data.id);
    state = immutable.del(state, ['list', idx]);
    let isLogin = this.store.selectSnapshot(SystemSelector.isLogin);
    let request;
    if (isLogin) {
      let token = this.store.selectSnapshot(SystemSelector.token) || '';
      const headers = {
        'Authorization': token,
      };
      request = this.http.delete(`/api/codeforfun-statistics-backend/data/deleteMenu?id=${data.id}`, {headers});
    } else {
      request = of({});
    }
    return request.pipe(tap(() => {
      ctx.patchState(state);
      this.message.create('success', `删除成功`);
    }), mergeMap(() => ctx.dispatch(new DemoAction.LoadMenu())));
  }

  @Action(DemoAction.ImportFromClipboard)
  async ImportFromClipboard(ctx: StateContext<DemoStateModel>) {
    let text = await navigator.clipboard.readText();
    return ctx.dispatch(new DemoAction.ImportSetting(text));
  }
}
